// Copyright 2011-2012, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_INTEGER_H_
#define LIBV_INTEGER_H_

#include <istream>
#include <ostream>


#define LIBV_DEFINE_ASSIGN_OPERATOR(op)                \
    DerivedType& operator op(const DerivedType& rhv) { \
        this->value_ op rhv.value_;                    \
        return *static_cast<DerivedType*>(this);       \
    }

#define LIBV_DEFINE_INT_ASSIGN_OPERATOR(op)      \
    DerivedType& operator op(const int rhv) {    \
        this->value_ op rhv;                     \
        return *static_cast<DerivedType*>(this); \
    }

#define LIBV_DEFINE_OPERATOR(op)                                  \
    const DerivedType operator op(const DerivedType& rhv) const { \
        return DerivedType(this->value_) op##= rhv;               \
    }

#define LIBV_DEFINE_INT_OPERATOR(op)                     \
    const DerivedType operator op(const int rhv) const { \
        return DerivedType(this->value_) op##= rhv;      \
    }

#define LIBV_DEFINE_PREFIX_UNARY_OPERATOR(op) \
    DerivedType operator op() const {         \
        return DerivedType(op this->value_);  \
    }

#define LIBV_DEFINE_COMPARISON_OPERATOR(op)          \
    bool operator op(const DerivedType& rhv) const { \
        return this->value_ op rhv.value_;           \
    }

#define LIBV_DEFINE_PREFIX_INC_DEC_OPERATOR(op)  \
    DerivedType& operator op() {                 \
        op this->value_;                         \
        return *static_cast<DerivedType*>(this); \
    }

#define LIBV_DEFINE_POSTFIX_INC_DEC_OPERATOR(op) \
    DerivedType operator op(int) {               \
       const DerivedType result(this->value_);   \
       this->value_ op;                          \
       return result;                            \
    }

namespace libv {
template<class TDerivedType, class TInt = int>
class TypedInteger {
  private:
    typedef TDerivedType DerivedType;

  public:
    typedef TInt Int;

    TypedInteger() : value_(0) {}
    ~TypedInteger() {}

    explicit TypedInteger(const TInt value)
        : value_(value) {}
    TypedInteger(const TypedInteger& rhv)
        : value_(rhv.value_) {}

    TInt& value() {
        return value_;
    }
    const TInt& value() const {
        return value_;
    }

    LIBV_DEFINE_ASSIGN_OPERATOR(=);

    LIBV_DEFINE_ASSIGN_OPERATOR(+=);
    LIBV_DEFINE_ASSIGN_OPERATOR(-=);
    LIBV_DEFINE_ASSIGN_OPERATOR(*=);
    LIBV_DEFINE_ASSIGN_OPERATOR(/=);

    LIBV_DEFINE_INT_ASSIGN_OPERATOR(*=);
    LIBV_DEFINE_INT_ASSIGN_OPERATOR(/=);

    LIBV_DEFINE_ASSIGN_OPERATOR(^=);
    LIBV_DEFINE_ASSIGN_OPERATOR(&=);
    LIBV_DEFINE_ASSIGN_OPERATOR(|=);

    LIBV_DEFINE_OPERATOR(+);
    LIBV_DEFINE_OPERATOR(-);
    LIBV_DEFINE_OPERATOR(*);
    LIBV_DEFINE_OPERATOR(/);

    LIBV_DEFINE_INT_OPERATOR(*);
    LIBV_DEFINE_INT_OPERATOR(/);

    LIBV_DEFINE_OPERATOR(^);
    LIBV_DEFINE_OPERATOR(&);
    LIBV_DEFINE_OPERATOR(|);

    LIBV_DEFINE_PREFIX_UNARY_OPERATOR(+);
    LIBV_DEFINE_PREFIX_UNARY_OPERATOR(-);
    LIBV_DEFINE_PREFIX_UNARY_OPERATOR(~);
    bool operator!() const { return !value_; }

    LIBV_DEFINE_COMPARISON_OPERATOR(==);
    LIBV_DEFINE_COMPARISON_OPERATOR(!=);
    LIBV_DEFINE_COMPARISON_OPERATOR(<);
    LIBV_DEFINE_COMPARISON_OPERATOR(>);
    LIBV_DEFINE_COMPARISON_OPERATOR(<=);
    LIBV_DEFINE_COMPARISON_OPERATOR(>=);

    LIBV_DEFINE_PREFIX_INC_DEC_OPERATOR(++);
    LIBV_DEFINE_PREFIX_INC_DEC_OPERATOR(--);

    LIBV_DEFINE_POSTFIX_INC_DEC_OPERATOR(++);
    LIBV_DEFINE_POSTFIX_INC_DEC_OPERATOR(--);

  private:
    TInt value_;
};

template<class TDerivedType, class TTInt>
inline ::std::ostream& operator<<(::std::ostream& os,
                                  const TypedInteger<TDerivedType, TTInt>& i) {
    return os << i.value();
}

template<class TDerivedType, class TTInt>
inline ::std::istream& operator>>(::std::istream& is,
                                  TypedInteger<TDerivedType, TTInt>& a) {
    TTInt value;
    is >> value;
    if ( !is.fail() ) {
        a = TypedInteger<TDerivedType, TTInt>(value);
    }
    return is;
}

}  // libv

#undef LIBV_DEFINE_POSTFIX_INC_DEC_OPERATOR
#undef LIBV_DEFINE_PREFIX_INC_DEC_OPERATOR
#undef LIBV_DEFINE_COMPARISON_OPERATOR
#undef LIBV_DEFINE_PREFIX_UNARY_OPERATOR
#undef LIBV_DEFINE_OPERATOR
#undef LIBV_DEFINE_ASSIGN_OPERATOR

#endif  // LIBV_INTEGER_H_
